"""
泰坦尼克号生存预测：
（预测型比赛流程）
1.pandas读入成DataFrame文件并且分析
2.用feature_column定义对离散特征(onehot)和连续特征的特征工程方式
3.DataFrame文件转成字典后用tf.data.Dataset.from_tensor_slices(数组 元组 字典)转成数据集 
4.用keras.layers.DenseFeature()和定义好的特征工程方式给训练数据train_x做特征工程
5.用keras建立模型训练或者转换成estimator训练（也可以用其他机器学习方法）
  estimator会把输出保存在一个文件夹中 包含tensorbroad 保存的模型 等


"""
import pandas as pd 
import matplotlib.pyplot as plt
import tensorflow as tf
from tensorflow import keras 

# 1.pandas读入成DataFrame文件并且分析
train_file = './tf_estimator/titanic/train.csv'
eval_file = './tf_estimator/titanic/eval.csv'

train_df = pd.read_csv(train_file)
eval_df = pd.read_csv(eval_file)

print(train_df.shape)  # (627,10) header有10个标题 其中9个特征 一个标签 
print(train_df.head())  # 了解header都是什么意思 找到要预测的列
train_y = train_df.pop('survived')  # 把要预测的列的数据提取出来单独做为标签
eval_y = eval_df.pop('survived')

print(train_df.describe())  # 查看一下哪些是数值型的数据 可以从里面挑选一些作为连续特征

# 数据分析 更加了解数据 对后期优化模型提供数据理解层面上的帮助 
# train_df['age'].hist()
# plt.show()
# train_df['sex'].hist()
# plt.show()
# print(pd.concat([train_df,train_y],axis=1).groupby('sex')['survived'].mean())  # 分组后查看各组的存活均值
# print(pd.concat([train_df,train_y],axis=1).groupby('sex')['age'].mean())

# 2.用feature_column定义对离散特征(onehot)和连续特征的特征工程方式
categorical_features = ['sex','n_siblings_spouses','parch','class','deck','embark_town','alone']  # 离散特征
numeric_features = ['age','fare']  # 连续特征
feature_columns = []  # 特征工程方式列表

for categorical_feature in categorical_features:
      vocab = train_df[categori_feature].unique()  # 获取当前离散特征的所有取值
      feature_columns.append(  
        tf.feature_column.categorical_column_with_vocabulary_list(categorical_feature,vocab)
      )  # 用feature_coumn做离散特征处理 然后加入特征工程方式列表中

for numeric_feature in numeric_features:
      feature_columns.append(
        tf.feature_column.numeric_column(numeric_feature,dtype=tf.float32)
      )  # 用feature_coumn做连续特征处理 然后加入特征工程方式列表中

# 3.DataFrame文件转成字典后用tf.data.Dataset.from_tensor_slices(数组 元组 字典)转成数据集 
def make_datasets(train_df,train_y,epochs=10,shuffle=True,batch_size=32):
      datasets = tf.data.Dataset.from_tensor_slices((dict(train_df),train_y))  # 训练数据有特征 用字典表示
      if shuffle:
            datasets = datasets.shuffle(10000)
      datasets = datasets.repeat(epochs).batch(batch_size)

      return datasets

train_datasets = make_datasets(train_df,train_y)
eval_datasets = make_datasets(eval_df,eval_y)

# 4.用keras.layers.DenseFeature()和定义好的特征工程方式给训练数据train_x做特征工程
# 5.用keras建立模型训练或者转换成estimator训练
model = keras.Sequential([
  keras.layers.DenseFeatures(feature_columns),  # 特征工程层
  keras.layers.Dense(16,activation='selu'),
  keras.layers.Dense(2,activation='softmax')
])

model.compile(loss=keras.losses.sparse_categorical_crossentropy,optimizer=keras.optimizers.Adam(0.01))
model.fit(
  train_datasets,validation_data=eval_datasets,epochs=10
  steps_per_epoch=627//32,validation_steps=265//32
  )

# 转换estimator
# 训练时要输入一个函数 这个函数没有输入 返回的是一个tensorflow的数据集
# 用lambda匿名函数把make_datasets封装成一个不要输入的函数 实际输入则在内部传入
estimator = keras.estimator.model_to_estimator(model)  # 转换
estimator.train(input_fn=lambda : make_datasets(train_df,train_y,epochs=10))








